Za cel pierwszej pracy domowej postanowiłem przeprowadzić eksplorację zbioru Bank Marketing.
https://www.mldata.io/dataset-details/bank_marketing/
Wpierw wczytajmy pakiety potrzebne do wykonania owego zadania.
import pandas as pd
import numpy as np
import sklearn
import matplotlib.pyplot as plt
Za pomocą pakietu pandas odtwórzmy i spójrzmy na nasz zbiór "na szybko".
data = pd.read_csv('bank_marketing_weka_dataset.csv')
data.sample(10)
data.shape
Nasze dane dotyczą informacji o klientach pewnego banku, a także kilku ich czynności powiązanych z ową instytucją. Możemy w nim między innymi wyczytać o ich wieku, pracy i statusie materialnym, ale także o czasu trwania ich ostatniego kontaktu z bankiem czy ilości takich przeprowadzonych rozmów.
Godne uwagi są także kolumny poutcome i y - pierwsza dotyczy sukcesu ostatniej kampanii marketingowej wobec owego klienta (?) (można spodziewać się, że idzie o kwestię, czy ostatnio subskrypcja została dokonana), zaś ta druga to spodziewana odpowiedź na pytanie czy klient dokona subskrypcji.
Co ciekawe, w naszym zbiorze nie mamy do czynienia ze standardowymi wartościami NA - tu zamiast tego pojawiają się komórki "unknown". Dla wygody i świadomości istnienia funkcji właśnie pod NA, zmodyfikujmy wszystkie brakujące wartości właśnie na NA. Ale czym będzie NA w Pythonie? W sumie nie wiem, ale ten issue ze Stack Overflow: https://stackoverflow.com/questions/28654325/what-is-pythons-equivalent-of-rs-na ... sugeruje mi, że chyba NaN.
Ile w ogóle tych wartości nieznanych się znajdzie
data = data.replace("unknown", np.nan)
data.sample(10)
Ile w ogóle tych wartości nieznanych się znajdzie?
data.transpose().isna().mean(axis=1).reset_index()
Oho! Okazuje się że brakuje śladowych informacji o pracy (mniej niż 1%), kilka procent poziomu edukacji, zaś do około 3 na 10 klientów nie ma informacji o formie kontaktu i w większości nie mamy danych o tym jak zakończyła się ostatnia kampania marketingowa.
Zacznę prosto, acz [moim zdaniem] ciekawie.
Kto prędzej sięgnie po subskrypcję? Singiel, małżonek, a może rozwodnik?
data_1 = data.copy()[["marital", "y"]]
data_1.sample(10)
data_1 = data_1.groupby(['marital','y']).size().reset_index(name = 'Count')
data_yes = data_1[data_1["y"] == "yes"].reset_index()
data_no = data_1[data_1["y"] == "no"].reset_index()
data_1 = data_yes
data_1["percentage"] = data_yes["Count"] / data_no["Count"] * 100
data_1[["marital", "percentage"]]
plt.bar(data_1["marital"], data_1["percentage"])
plt.xlabel('marital status')
plt.ylabel('percentage of predicted subscribers')
plt.title('Predictor class by civil status')
plt.show()
Co ciekawe bardzo podobny odsetek ludzi samotnych i rozwiedzionych jest zapowiadany na subskrypcję. Znacząco rzadziej można się spodziewać osób w związkach małżeńskich. A wydawałoby się, że to małżeństwom sprzyja częstsze branie kredytu! Na dom i wgl
Skorzystajmy z numpy-owskiej funkcji corr() w celu wyliczenia zależności między kolumnami.
data.corr()
W większości przypadków współczynnik korelacji okazał się być bliski zeru. Co ciekawe stosunkowo wysoką (?) korelację mają kolumny campaign i day - o tyle jest to ciekawe, że pierwsza dotyczy liczby wykonanych kontaktów, a druga dnia ostatniego.
Czy wynika to z tego, że dane zostały zebrane konkretnego dnia i klienci bardziej aktywni z większym prawdopodobieństwem zgadali się z bankiem także i w ostatnim czasie? Sprawdźmy to.
plt.scatter(data["campaign"], data["day"], s = 30)
plt.xlabel('days passed since client was last contacted')
plt.ylabel('day of the month')
plt.title('Predictor class by civil status')
plt.show()
Tak sobie. Uśrednijmy wartości
data_2 = data.copy()[["campaign", "day"]]
data_2_1 = data_2.groupby("campaign").mean().reset_index()
data_2_2 = data_2.groupby("day").mean().reset_index()
plt.scatter(data_2_1["campaign"], data_2_1["day"])
plt.xlabel('days passed since client was last contacted')
plt.ylabel('mean day of the month')
plt.show()
plt.scatter(data_2_2["day"], data_2_2["campaign"])
plt.xlabel('day of the month')
plt.ylabel('mean number of days since last contact')
plt.show()
Chyba możemy wnioskować z tego, że dane zostały zebrane około połowy miesiąca.
No osobiście obstawiam, że jak klient wcześniej dokonał subskrypcji, teraz prędzej dokona - zaś ci bojaźliwi jak byli na nie, tak się nie przekonają.
data_3 = data.copy()[["poutcome", "y"]]
data_3.groupby(["poutcome", "y"]).size().reset_index()
I jest tak jak mogliśmy się spodziewać - absolutnej większości klientów, która wcześniej nie wyraziła chęci, również i teraz nie wróżymy zdecydowania się na subskrypcję, zaś dla tych co wcześniej byli na tak - w około 2/3 przypadkach liczymy na przedłużenie.
Strzelam, że ludzie starsi będą mieli większą tendencję do dłuższej rozmowy! Czy moja intuicja się sprawdzi? Zbadajmy to!
data_4 = data.copy()[["age", "duration"]]
data_4 = data_4.groupby("age").mean().reset_index()
plt.plot(data_4["age"], data_4["duration"])
plt.xlabel('client\'s age')
plt.ylabel('mean contact time')
plt.show()
Tu akurat nie widać nic ciekawego. Coraz większe zawirowania wraz z wiekiem wynikają z małej ilości danych dla kolejnych liczb lat.
data_5 = data.copy()[["balance", "duration"]]
plt.scatter(data_5[["balance"]], data_5[["duration"]], s = 1)
plt.title('Average yearly balance in Euro by last contact duration')
plt.xlabel('yearly balance in Euro')
plt.ylabel('last contact duration')
plt.show()
Interesująca sprawa - wygląda na to, że wraz z większym balansem konsultacje trwają krócej. Jak widać ludzie sukcesu nie tracą czasu na głupie rozmówki!
Zacząłem się bawić pandas-em, więc chyba nic dziwnego, że zdecydowałem się na pandas-profiling. Jest to bardzo wygodne narzędzie umożliwiające szybkie zrozumienie analizowanych danych.
Zanim weźmiemy się za narzędzie od pandas-profiling, obczajmy efekt dla funkcji describe() od pandas-a.
data.describe()
Niby wszystko spoko, ale co z wartościami nienumerycznymi? Przekonwertujmy je do typu numeric.
data_cat = data.copy()
data_cat["job"] = pd.factorize(data_cat['job'])[0] + 1
data_cat["marital"] = pd.factorize(data_cat['marital'])[0] + 1
data_cat["education"] = pd.factorize(data_cat['education'])[0] + 1
data_cat["default"] = pd.factorize(data_cat['default'])[0] + 1
data_cat["housing"] = pd.factorize(data_cat['housing'])[0] + 1
data_cat["loan"] = pd.factorize(data_cat['loan'])[0] + 1
data_cat["contact"] = pd.factorize(data_cat['contact'])[0] + 1
data_cat["month"] = pd.factorize(data_cat['month'])[0] + 1
data_cat["poutcome"] = pd.factorize(data_cat['poutcome'])[0] + 1
data_cat["y"] = pd.factorize(data_cat['y'])[0] + 1
data_cat.describe()
Okey, teraz kolumny niektóre nie mają sensu [patrz: chociażby miesiąc], ale jakieś informacje z tego dostaniemy.
Co w przypadku pandas_profiling?
import pandas_profiling
pandas_profiling.ProfileReport(data)
Omg, przepiękne to jest!
Kocham pandas-profiling!
O czym się dowiedzieliśmy?
To by było na tyle! Ahh pandas-profilling, czuję że będę do ciebie wracał